/*
DS18B20 驱动程序源文件
*/

//##################################################################################################
// 头文件包含
#include "ds18b20.h"
#include "zsf_delay.h"
#include "stdio.h"


//##################################################################################################
// 宏定义
#define IN	1 // DS18B20 输入模式
#define OUT 2	// DS18B20 输出模式

//##################################################################################################
// 变量定义
DS18B20_TypeDef ds18b20;

//##################################################################################################
// 函数声明
static void ds18b20_Mode(uint8_t in_or_out); // DS18B20 模式设置，输入|输出
static void ds18b20_Write_Byte(uint8_t data); // 向DS18B20写入一个字节数据
static uint8_t ds18b20_Read_Byte(void); // 从DS18B20读取一个字节数据

//##################################################################################################
// 初始化 DS18B20
// 返回0：初始化成功
// 返回1：初始化失败
uint8_t ds18b20_Init(void)
{
	uint16_t DS_flag; 		// DS18B20 初始化是否成功
	ds18b20_Mode(OUT); 		// 设置 DS18B20 为输出模式
	DS18B20_WRITE(0);		// 输出 0
	Delay_us(600);			// 延时 600u 秒
	DS18B20_WRITE(1);		// 输出 1
	Delay_us(30);			// 延时 30u 秒
	ds18b20_Mode(IN); 		// 设置 DS18B20 为输入模式
	DS_flag = DS18B20_READ;
	Delay_us(500);			// 延时 500u 秒
	return DS_flag;
}

//##################################################################################################
// 获取温度
float ds18b20_GetTemp(void) // 获取最终的温度
{
	/*
    读到的温度数据 Byte0,Byte1 (12位精度)
    TempL  2^3  2^2  2^1  2^0  2^-1  2^-2  2^-3  2^-4
    TempH   S    S    S    S    S    2^6   2^5   2^4
    S=0(温度为正)  S=1(温度为负)
    */
	uint8_t tempL;
	uint8_t tempH;
	
	ds18b20_Init();
	ds18b20_Write_Byte(0xcc); //跳过读取序列号(ROM)
	ds18b20_Write_Byte(0x44); //启动温度检测
	
	HAL_Delay(800);
	
	ds18b20_Init();
	ds18b20_Write_Byte(0xcc);
	ds18b20_Write_Byte(0xbe); //读取温度寄存器内容(读取头两个值为温度)
	
	tempL = ds18b20_Read_Byte(); //读取Byte0温度数据
	tempH = ds18b20_Read_Byte(); //读取Byte1温度数据
	ds18b20.tempHLData = (tempH << 8) | tempL;
	#if(_DS18B20_DEBUG == 1)
	printf("tempHLData: %d\r\n", ds18b20.tempHLData);
	#endif
	ds18b20.temp = ds18b20.tempHLData;
	if(tempH > 7)
	{
		ds18b20.tempHLData = ~ds18b20.tempHLData + 1;
	}
	ds18b20.temp = ds18b20.temp * 0.0625 + 0.5;
	
	#if(_DS18B20_DEBUG == 1)
	printf("temp: %.2f\r\n", ds18b20.temp);
	#endif
	
	return ds18b20.temp;
}

//##################################################################################################
// 设置 DS18B20 IO 方向
// in_or_out: 控制 IO 口的方向标志，1: 输入 2：输出
static void ds18b20_Mode(uint8_t in_or_out)
{
	GPIO_InitTypeDef GPIO_InitStruct; // 定义一个 GPIO 结构
	GPIO_InitStruct.Pin = DS18B20_PIN;
	// in_or_out == 1 时，设置DS18B20 IO 位输入
	if(in_or_out == 1)
	{
		GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
	} else if(in_or_out == 2) // in_or_out == 1 时，设置DS18B20 IO 位输出
	{
		GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; // 设置位开漏输出模式
		GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
	}
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	HAL_GPIO_Init(DS18B20_PORT, &GPIO_InitStruct);
}

//##################################################################################################
// 向DS18B20写入一个字节数据
static void ds18b20_Write_Byte(uint8_t data)
{
	uint8_t i;
	for(i=8; i>0; i--)
	{
		ds18b20_Mode(OUT);
		DS18B20_WRITE(0); // 开始发送
		Delay_us(2); // 至少延迟 1us
		if((data & 0x01) ==1)
		{
			DS18B20_WRITE(1);
		} else
		{
			DS18B20_WRITE(0);
		}
		Delay_us(70); // 持续至少 60us
		DS18B20_WRITE(1);
		data >>= 1;
	}
}

//##################################################################################################
// 从DS18B20读取一个字节的数据
static uint8_t ds18b20_Read_Byte(void)
{
	uint8_t i;
	uint8_t data = 0;
	// 1. data >>= 1 右移一位数据抛弃低位的数据
	// 2. data |= 0x80; // data 与 0x80 按位或运算，最高位写1
	// 两者配合完成数据的接收，从低到高接收
	for(i=8; i>0; i--)
	{
		ds18b20_Mode(OUT);
		DS18B20_WRITE(0);
		Delay_us(2);
		DS18B20_WRITE(1);
		Delay_us(15);
		data >>= 1; // 抛弃最低位
		if(DS18B20_READ ==1)
		{
			data |= 0x80; // data 与 0x80 按位或运算，最高位写1
		}
		Delay_us(50);
	}
	return data;
}

